package com.mge.humitureproj.schedule;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fazecast.jSerialComm.SerialPort;
import com.mge.humitureproj.entity.HumitureDeploy;
import com.mge.humitureproj.entity.HumitureExRecord;
import com.mge.humitureproj.entity.HumitureRealtimeData;
import com.mge.humitureproj.entity.HumitureRecord;
import com.mge.humitureproj.service.HumitureDeployService;
import com.mge.humitureproj.service.HumitureExRecordService;
import com.mge.humitureproj.service.HumitureRealtimeDataService;
import com.mge.humitureproj.service.HumitureRecordService;
import com.mge.humitureproj.utils.ModbusUtil;
import com.mge.humitureproj.utils.VoiceAnnouncementsUtil;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.*;

@Component
@Slf4j
public class ModbusPoller {

    private String portName;

    @Autowired
    private HumitureDeployService humitureDeployService;

    @Autowired
    private HumitureRecordService humitureRecordService;

    @Autowired
    private HumitureRealtimeDataService humitureRealtimeDataService;

    @Autowired
    private HumitureExRecordService humitureExRecordService;

    @Value("${serial.baud-rate:9600}")
    private int baudRate;

    private SerialPort serialPort;
    // 缓存最近读取的数据
    private Map<Integer, int[]> dataCache = new ConcurrentHashMap<>();
    // 线程锁
    private final Object lock = new Object();

    private boolean isRunning = false;

    private Map<Integer, ModbusRequest> requestMap = new ConcurrentHashMap<>();

    private Map<Integer, HumitureRealtimeData> realTimeDataMap = new ConcurrentHashMap<>();

    private List<Integer> addressList = Arrays.asList(1,2,3);

    private List<HumitureDeploy> list ;

    private Map<Integer,HumitureDeploy> deployMap = new ConcurrentHashMap<>();

    // 标识符
    private Map<Integer,Integer> flagMap = new ConcurrentHashMap<>();

    private String ip;

    @PostConstruct
    public void init() {
        // Windows与Linux的主要区别：串口设备路径
        portName = System.getProperty("os.name").contains("Windows")
                ? "COM3"  // Windows路径
                : "/dev/ttyUSB0";  // Linux路径
        // 初始化串口
        serialPort = SerialPort.getCommPort(portName);
        serialPort.setBaudRate(baudRate);
        serialPort.setNumDataBits(8);
        serialPort.setNumStopBits(SerialPort.ONE_STOP_BIT);
        serialPort.setParity(SerialPort.NO_PARITY);

        // 获取本机ip
        try {
            ip = ModbusUtil.getLocalIp();
            if (ip.isEmpty()||ip.equals("127.0.0.1")){
                System.exit(0);
                return;
            }
            log.info("获取到的ip："+ip);
            list = humitureDeployService.list(new QueryWrapper<HumitureDeploy>().eq("regional_id",ip).eq("type", "cj"));
            if (!list.isEmpty()){
                addressList = list.stream().map(HumitureDeploy::getAddress).toList();
                // 创建部署信息缓存
                deployMap = list.stream().collect(
                                (()->deployMap),
                                (map,humitureDeploy)->map.put(humitureDeploy.getAddress(),humitureDeploy),
                                (map1,map2)->map1.putAll(map2)
                        );
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        if (serialPort.openPort()) {
            log.info("串口已打开: " + portName);
            // 启动返回数据监听
            startListening();
            // 启动超时监控
            startTimeoutMonitor();
        } else {
            log.error("无法打开串口: " + portName);
        }
    }


    /**
     * 发送 Modbus 读寄存器请求
     */
    @Scheduled(cron = "0/30 * * * * ?")
    public void readRegisters() {
        // 检查串口连接状态
        if (!serialPort.isOpen()) {
            try {
                if (!serialPort.openPort()) {
                    log.error("串口打开失败，跳过本次任务");
                    return;
                }
            } catch (Exception e) {
                log.error("初始化串口异常", e);
                return;
            }
        }

        addressList.forEach(address -> {
            long timestamp = System.currentTimeMillis();
            try {
                byte[] request = ModbusUtil.buildReadRegistersRequest(address, 0, 2);
                ModbusRequest modbusRequest = new ModbusRequest(request, new ResponseCallback() {
                    @Override
                    public void onSuccess(int[] registers) {
                        log.info(address+"号寄存器:"+ Arrays.toString(registers));
                        // 运行时间
                        synchronized (lock){
                            dataCache.put(address,registers);
                            // 判断数据是否异常，若是异常，改变标识符，1为异常，0为正常   异常则上传异常数据
                            HumitureDeploy humitureDeploy = deployMap.get(address);

                            if (humitureDeploy==null||registers.length == 0){
                                return;
                            }
                            HumitureRealtimeData humitureRealtimeData = new HumitureRealtimeData();
                            humitureRealtimeData.setRegionalId(humitureDeploy.getId());
                            setRealTimeValues(humitureRealtimeData, registers);
                            realTimeDataMap.put(humitureDeploy.getId(),humitureRealtimeData);

                            // registers[0]为湿度，registers[1]为温度
                            if (registers[0]>humitureDeploy.getHumidityUpLimit().floatValue()*10||registers[0]<humitureDeploy.getHumidityDownLimit().floatValue()*10
                                || registers[1]>humitureDeploy.getTemperatureUpLimit().floatValue()*10||registers[1]<humitureDeploy.getTemperatureDownLimit().floatValue()*10
                            ){
                                // 在之前若是正常，则上传异常数据,flagMap.get(address)数据若是null，则表示一开软件就是异常情况
                                // 只上传一次异常数据
                                if (flagMap.get(address)==null||flagMap.get(address)==0) {
                                    // 改变标识符状态，状态为异常
                                    flagMap.put(address,1);
                                    saveAbnormalRecord(registers, humitureDeploy,"ng");
                                }
                            }else {
                                // 改变标识符状态，状态为正常
                                if (flagMap.get(address)!=null&&flagMap.get(address)==1){
                                    // 结束异常
                                    saveAbnormalRecord(registers, humitureDeploy,"ok");
                                }
                                // 0 为正常，1为异常
                                flagMap.put(address,0);
                            }
                            log.info("耗时时间："+ (System.currentTimeMillis()-timestamp));
                        }
                    }

                    @Override
                    public void onError(String message) {
                        // 处理错误（异步回调）
                        VoiceAnnouncementsUtil.announce("警告、读取失败");
                        log.error("读取失败: "+message);
                    }
                });
                requestMap.put(address,modbusRequest);
                sendData(modbusRequest.data);
                Thread.sleep(50);
            }catch (Exception e){
                log.error("发送请求异常 "+e.getMessage());
            }
        });
        // 更新实时值
        long timestamp = System.currentTimeMillis();
        updateRealTimeData();
        log.info("实时更新耗时："+ (System.currentTimeMillis()-timestamp));
    }

    //  更新实时数据
    private void updateRealTimeData() {
        List<HumitureRealtimeData> updateList = new ArrayList<>();
        List<HumitureRealtimeData> addList = new ArrayList<>();

        // 获取所有设备配置id
        List<Integer> regionalIds = realTimeDataMap.keySet().stream().toList();
        if (regionalIds.isEmpty()) {
            return;
        }
        QueryWrapper<HumitureRealtimeData> wrapper = new QueryWrapper<>();
        // 根据设备配置id，查出当前有多少个配置，配置id唯一
        List<HumitureRealtimeData> humitureRealtimeData = humitureRealtimeDataService.list(wrapper.in("regional_id", regionalIds));

        // 判读查出来的id 跟 保存在map中的id是否相配
        realTimeDataMap.forEach((id,data)->{
            if (humitureRealtimeData.isEmpty()){
                addList.add(data);
            }else {
                humitureRealtimeData.forEach(item->{
                    if (item.getRegionalId().equals(id)){
                        data.setId(item.getId());
                        updateList.add(data);
                    }
                });
            }
        });
        if (addList.size()>0){
            humitureRealtimeDataService.insertBatch(addList);
        }
        if (updateList.size()>0){
            humitureRealtimeDataService.updateBatchById(updateList);
        }
    }

    private void setRealTimeValues(HumitureRealtimeData data, int[] registers) {
        data.setRealTimeHumidityValue(new BigDecimal(registers[0]/10.0f).setScale(1, RoundingMode.HALF_UP));
        data.setRealTimeTemperatureValue(new BigDecimal(registers[1]/10.0f).setScale(1, RoundingMode.HALF_UP));
    }


    /**
     * 保存异常数据
     * @param registers  采集数据
     * @param humitureDeploy  异常区域信息
     */
    private void saveAbnormalRecord(int[] registers, HumitureDeploy humitureDeploy,String flag) {
        // 异步保存异常记录，避免阻塞主线程
        CompletableFuture.runAsync(() -> {
            try {
                if (flag.equals("ng")) {
                    HumitureExRecord record = new HumitureExRecord();
                    record.setRecordDate(new Date());
                    // 温度值
                    record.setRecordTemperatureValue(new BigDecimal(registers[1] / 10.0f).setScale(1, RoundingMode.HALF_UP));
                    // 湿度值
                    record.setRecordHumidityValue(new BigDecimal(registers[0] / 10.0f).setScale(1, RoundingMode.HALF_UP));
                    record.setRegionalIdAddress(humitureDeploy.getId().toString());
                    humitureExRecordService.save(record);
                    log.info("保存异常记录成功 - 区域: {}", record.toString());
                }else if (flag.equals("ok")){
                    // 结束异常时间
                    HumitureExRecord recordServiceOne = humitureExRecordService.selectByRegionalId(humitureDeploy.getId().toString());
                    recordServiceOne.setRecordEndDate(new Date());
                    recordServiceOne.updateById();
                }
            } catch (Exception e) {
                log.error("保存异常记录失败", e);
            }
        });
    }


    @Scheduled(cron = "0 0/30 * * * ?")
    public void saveToDatabase(){
        synchronized (lock){
            if (dataCache.isEmpty()){
                log.info("没有新数据需要写入数据库");
                return;
            }
            List<HumitureRecord> dataList = new ArrayList<>();
            log.info("开始写入数据库"+requestMap.toString());
            deployMap.forEach((address,humitureDeploy)->{
                HumitureRecord humitureRecord = new HumitureRecord();
                humitureRecord.setId(null);
                humitureRecord.setRegionalIdAddress(humitureDeploy.getId().toString());
                humitureRecord.setRecordDate(new Date());
                int[] ints = dataCache.get(address);
                if (ints!=null) {
                    // 湿度值
                    humitureRecord.setRecordHumidityValue(new BigDecimal(ints[0] / 10.0f).setScale(1, RoundingMode.HALF_UP));
                    // 温度值
                    humitureRecord.setRecordTemperatureValue(new BigDecimal(ints[1] / 10.0f).setScale(1, RoundingMode.HALF_UP));
                }
                dataList.add(humitureRecord);
                log.info("写入数据库: "+humitureRecord.toString()+" 号寄存器: "+ Arrays.toString(ints));
            });
            humitureRecordService.insertBatch(dataList);
            log.info("已写入数据库，共{}条记录",dataList.size());
            dataCache.clear(); // 清空缓存
        }
    }

    /**
     * 监听串口数据
     */
    private void startListening() {
        isRunning = true;
        new Thread(() -> {
            byte[] buffer = new byte[1024];
            ByteArrayOutputStream responseBuffer = new ByteArrayOutputStream();

            while (isRunning) {
                try {
                    if (serialPort.bytesAvailable() > 0) {
                        int bytesRead = serialPort.readBytes(buffer, buffer.length);
                        responseBuffer.write(buffer, 0, bytesRead);

                        // 解析响应（根据 Modbus 协议特性：固定头+字节数指示）
                        processResponseBuffer(responseBuffer);
                    }
                    Thread.sleep(10); // 避免 CPU 占用过高
                } catch (Exception e) {
                    log.error("读取串口数据异常："+e.getMessage());
                }
            }
        }).start();
    }

    /**
     * 启动超时监控
     */
    private void startTimeoutMonitor() {
        new Thread(() -> {
            while (isRunning) {
                try {
                    Thread.sleep(500); // 每 500ms 检查一次
                    long now = System.currentTimeMillis();
                    requestMap.forEach((address, request) -> {
                        if (now - request.timestamp > 1000) { // 超时 1s
                            if (request.callback != null) {
                                request.callback.onError("请求地址 " + address + " 超时");
                            }
                            requestMap.remove(address); // 移除已处理的请求
                        }
                    });
                } catch (Exception e) {
                    log.error("超时监控异常：" + e.getMessage());
                }
            }
        }).start();
    }

    /**
     * 处理响应缓冲区
     */
    private void processResponseBuffer(ByteArrayOutputStream buffer) {
        byte[] data = buffer.toByteArray();

        // 检查是否有完整的 Modbus 响应（至少 5 字节：地址+功能码+字节数+数据+CRC）
        if (data.length >= 5) {
            int responseLength = getModbusResponseLength(data);
            if (responseLength > 0 && data.length >= responseLength) {
                // 提取完整响应
                byte[] response = Arrays.copyOf(data, responseLength);

                // 验证 CRC
                if (ModbusUtil.verifyCRC(response)) {
                    int slaveAddress = response[0] & 0xFF; // 提取从站地址
                    int functionCode = response[1] & 0xFF;

                    if (functionCode == 0x03 || functionCode == 0x04) {
                        try {
                            int[] registers = ModbusUtil.parseReadRegistersResponse(response);

                            ModbusRequest request = requestMap.remove(slaveAddress);
                            if (request != null && request.callback != null) {
                                request.callback.onSuccess(registers);
                            }

                        } catch (Exception e) {
                            log.error("解析 Modbus 响应失败: " + e.getMessage());
                        }
                    } else {
                        log.error("不支持的功能码: " + functionCode);
                    }
                } else {
                    log.error("CRC 校验失败");
                }

                // 从缓冲区移除已处理的数据
                buffer.reset();
                if (data.length > responseLength) {
                    try {
                        buffer.write(Arrays.copyOfRange(data, responseLength, data.length));
                    } catch (IOException e) {
                        log.error("写入缓冲区异常："+e.getMessage());
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }

    /**
     * 获取 Modbus 响应的完整长度
     */
    private int getModbusResponseLength(byte[] data) {
        if (data.length < 2) return -1;

        int functionCode = data[1] & 0xFF;
        if (functionCode == 0x03 || functionCode == 0x04) {
            int byteCount = data[2] & 0xFF;
            return 3 + byteCount + 2; // 地址+功能码+字节数+数据+CRC
        }
        return -1; // 未知功能码
    }


    /**
     * 发送数据到串口
     */
    private void sendData(byte[] data) {
        try (OutputStream out = serialPort.getOutputStream()) {
            System.out.println("发送数据: " + bytesToHex(data));
            out.write(data);
        } catch (IOException e) {
            log.error("发送数据失败："+e.getMessage());
        }
    }

    /**
     * 字节数组转十六进制字符串
     */
    private String bytesToHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(String.format("%02X ", b));
        }
        return result.toString().trim();
    }

    /**
     * Modbus 请求类
     */
    private static class ModbusRequest {
        byte[] data;
        ResponseCallback callback;

        long timestamp;

        public ModbusRequest(byte[] data, ResponseCallback callback) {
            this.data = data;
            this.callback = callback;
            this.timestamp = System.currentTimeMillis(); // 初始化时间戳
        }
    }

    /**
     * 响应回调接口
     */
    public interface ResponseCallback {
        void onSuccess(int[] registers);
        void onError(String message);
    }

}
